//
//  BluetoothPeripheralManager.m
//  BluetoothDemo
//
//  Created by Niko on 2025/8/26.
//
#import "BluetoothPeripheralManager.h"

@implementation BluetoothPeripheralManager

#pragma mark - 单例与初始化

/**
 * 获取蓝牙外设管理器的单例实例
 * 采用 GCD 的 dispatch_once 确保线程安全
 * @return BluetoothPeripheralManager 的单例对象
 */
+ (instancetype)sharedInstance {
    static BluetoothPeripheralManager *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        sharedInstance.bluetoothName = @"测试名称";
    });
    return sharedInstance;
}

/**
 * 初始化方法
 * 主要初始化 CBPeripheralManager 核心对象并设置其代理
 */
- (instancetype)init {
    self = [super init];
    if (self) {
        // 初始化核心的外设管理器对象
        // queue 参数传入 nil 表示使用主队列（Main Queue）进行代理回调
        _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
        _isAdvertising = NO; // 初始化时广告状态为未开启
    }
    return self;
}

#pragma mark - 公开方法

/**
 * 开始蓝牙广播
 * 此方法会创建自定义服务和特征，并将其添加到本地数据库，最后启动广播
 * 注意：请在确认蓝牙状态为 CBManagerStatePoweredOn 后调用
 */
- (void)startAdvertising {
    // 检查蓝牙硬件是否就绪
    if (self.peripheralManager.state != CBManagerStatePoweredOn) {
        NSLog(@"CoreBluetooth 未就绪，当前状态码: %ld", (long)self.peripheralManager.state);
        return;
    }
    
    // 1. 创建特征 (Characteristic)
    CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID];
    // 定义特征的属性：可读、可写、可通知（订阅）
    CBCharacteristicProperties properties = (CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite | CBCharacteristicPropertyNotify);
    // 定义特征的权限：可读、可写
    CBAttributePermissions permissions = (CBAttributePermissionsReadable | CBAttributePermissionsWriteable);
    
    // 初始化可变特征。value 传入 nil 表示动态处理读写请求，而非静态值。
    _customCharacteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID
                                                               properties:properties
                                                                    value:nil
                                                              permissions:permissions];
    
    // 2. 创建服务 (Service)
    CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
    // 初始化一个主服务 (primary: YES)
    _customService = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
    // 将特征添加到服务的特征数组中
    _customService.characteristics = @[self.customCharacteristic];
    
    // 3. 将服务发布（添加）到设备的本地 GATT 数据库
    // 添加成功后，会触发 `peripheralManager:didAddService:error:` 代理回调
    [self.peripheralManager addService:self.customService];
    
    // 4. 开始广播的操作将在上面的代理回调中执行
}

/**
 * 停止蓝牙广播
 * 调用此方法会立即停止广播，其他设备将无法再扫描到本设备
 */
- (void)stopAdvertising {
    [self.peripheralManager stopAdvertising];
    self.isAdvertising = NO;
    NSLog(@"广播已停止");
}

#pragma mark - CBPeripheralManagerDelegate

/**
 * 必实现的代理方法：监听外设管理器的状态变化
 * 这是整个蓝牙功能的基础，任何操作前都应检查状态
 * @param peripheral 状态发生变化的 PeripheralManager 对象
 */
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    NSString *stateString = nil;
    switch (peripheral.state) {
        case CBManagerStatePoweredOn:
            stateString = @"蓝牙已开启，可用";
            // 状态为开启，可以进行后续的添加服务、广播等操作
            break;
        case CBManagerStatePoweredOff:
            stateString = @"蓝牙已关闭";
            [self stopAdvertising]; // 蓝牙关闭，自动停止广播
            break;
        case CBManagerStateUnauthorized:
            stateString = @"应用未获得使用蓝牙的授权";
            break;
        case CBManagerStateUnsupported:
            stateString = @"该设备不支持蓝牙低功耗(BLE)";
            break;
        case CBManagerStateResetting:
            stateString = @"蓝牙系统正在重置，稍后会有状态更新";
            break;
        default:
            stateString = @"蓝牙状态未知";
            break;
    }
    NSLog(@"外设管理器状态更新: %@", stateString);
}

/**
 * 代理方法：服务添加完成回调
 * 当调用 `addService:` 方法完成后，会触发此回调
 * @param peripheral 执行添加服务的 PeripheralManager 对象
 * @param service 被添加的服务
 * @param error 添加过程中发生的错误，如果成功则为 nil
 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
    if (error) {
        NSLog(@"发布服务时发生错误: %@", [error localizedDescription]);
        return;
    }
    NSLog(@"服务发布成功: %@", service.UUID);
    
    // 服务发布成功后，正式开始广播
    // 构造广播数据字典
    NSDictionary *advertisingData = @{
        CBAdvertisementDataLocalNameKey : self.bluetoothName, // 设备广播的本地名称
        CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:kServiceUUID]], // 广播的服务UUID数组
    };
    
    // 开始广播
    [self.peripheralManager startAdvertising:advertisingData];
}

/**
 * 代理方法：广播启动完成回调
 * 当调用 `startAdvertising:` 方法完成后，会触发此回调
 * @param peripheral 执行广播的 PeripheralManager 对象
 * @param error 启动广播过程中发生的错误，如果成功则为 nil
 */
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
    if (error) {
        NSLog(@"启动广播时发生错误: %@", [error localizedDescription]);
        return;
    }
    self.isAdvertising = YES;
    NSLog(@"广播已成功启动！其他设备现在可以扫描到我们。");
}

// MARK: - 与中心设备交互的核心代理方法

/**
 * 处理读请求回调
 * 当中心设备尝试读取本外设特征的 value 时触发
 * 必须调用 `respondToRequest:withResult:` 来响应此请求
 * @param peripheral 接收请求的 PeripheralManager 对象
 * @param request 封装了读请求信息的 CBATTRequest 对象
 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
    // 检查请求读取的特征是否是我们自定义的特征
    if ([request.characteristic.UUID isEqual:self.customCharacteristic.UUID]) {
        
        // 验证请求的偏移量是否有效（不超过数据的长度）
        if (request.offset > self.customCharacteristic.value.length) {
            [peripheral respondToRequest:request withResult:CBATTErrorInvalidOffset];
            return;
        }
        
        // 准备要返回给中心设备的数据
        // 例如：返回一个字符串 "你好，中心设备！"
        NSString *responseString = @"你好，中心设备！";
        NSData *responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
        
        // 将数据设置到请求对象中
        request.value = responseData;
        
        // 响应请求，返回成功结果
        [peripheral respondToRequest:request withResult:CBATTErrorSuccess];
        NSLog(@"已成功响应中心设备的读请求");
        
    } else {
        // 如果请求的特征不是我们持有的，返回未找到错误
        [peripheral respondToRequest:request withResult:CBATTErrorAttributeNotFound];
    }
}

/**
 * 处理写请求回调
 * 当中心设备尝试向本外设特征的 value 写入数据时触发
 * 必须调用 `respondToRequest:withResult:` 来响应此请求（响应数组中的第一个请求即可）
 * @param peripheral 接收请求的 PeripheralManager 对象
 * @param requests 包含多个写请求的 CBATTRequest 对象数组
 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
    // 遍历请求数组（通常一次一个请求）
    for (CBATTRequest *request in requests) {
        if ([request.characteristic.UUID isEqual:self.customCharacteristic.UUID]) {
            // 将从中心设备接收到的数据保存到特征中
            self.customCharacteristic.value = request.value;
            
            // 将接收到的数据解码为字符串并打印日志（实际应用可能是任何格式的数据）
            NSString *receivedString = [[NSString alloc] initWithData:request.value encoding:NSUTF8StringEncoding];
            NSLog(@"接收到来自中心设备的数据: %@", receivedString);
            
            // 在这里可以添加自定义逻辑来处理接收到的数据
            // 例如：解析指令、更新UI、发送通知等
        }
    }
    // 响应请求（通常响应第一个请求代表整个批量操作的結果）
    [peripheral respondToRequest:requests[0] withResult:CBATTErrorSuccess];
}

/**
 * 中心设备订阅特征回调
 * 当中心设备订阅了本外设的某个特征的通知（Notify）时触发
 * @param peripheral 相关的 PeripheralManager 对象
 * @param central 发起订阅的中心设备对象
 * @param characteristic 被订阅的特征
 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"中心设备已订阅特征: %@", characteristic.UUID);
    // 可以在这里启动定时器或机制，定期通过 updateValue:forCharacteristic:onSubscribedCentrals:
    // 方法向所有已订阅的中心设备发送数据更新（通知）
}

/**
 * 中心设备取消订阅特征回调
 * 当中心设备取消订阅本外设的某个特征的通知时触发
 * @param peripheral 相关的 PeripheralManager 对象
 * @param central 取消订阅的中心设备对象
 * @param characteristic 被取消订阅的特征
 */
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"中心设备已取消订阅特征: %@", characteristic.UUID);
    // 可以在这里停止相应的定时器或数据发送机制
}

@end


